package edu.kit.csl.cratylus.datatypes;

import java.util.Iterator;

import edu.kit.csl.cratylus.datatypes.SentencePair.AlignmentPair;

/**
 * A instance of this class represents a source language - target language
 * sentence pair and an alignment between them. The alignment is stored in
 * integer vector representation as in IBM models. The {@link Iterable} 
 * interface is implemented for iterating through the alignment (i.e. iterating
 * through all target/source word pairs in this sentence pair given the
 * alignment. 
 */
public class SentencePair implements Iterable<AlignmentPair> {

	/*
	 * Source and target language sentences
	 */
	private Sentence srcSentence, trgtSentence;
	
	/*
	 * The alignment in integer vector representation
	 */
	private short[] alignment;
	
	/**
	 * Sole constructor. Besides initializing member variables, mark the target
	 * words with their associated source word.
	 * 
	 * @param srcSentence the source sentence
	 * @param trgtSentence the target sentence
	 * @param alignment alignment in integer vector representation
	 * @see Sentence
	 * @throws IndexOutOfBoundsException if the target sentence length does not
	 * 		match the alignment array length
	 * @throws NullPointerException if one of the parameters is null
	 */
	public SentencePair(Sentence srcSentence, Sentence trgtSentence, short[] 
			alignment) throws IndexOutOfBoundsException, NullPointerException {
		if (alignment.length != trgtSentence.getLength()) {
			throw new IndexOutOfBoundsException();
		}
		if (srcSentence == null) {
			throw new NullPointerException();
		}
		this.srcSentence = srcSentence;
		this.trgtSentence = trgtSentence;
		this.alignment = alignment;
		updateRelatedReferences();
	}
	
	/*
	 * Update the related word references of the target words such that they
	 * point to the associated source word.
	 */
	private void updateRelatedReferences() {
		for (int i = 0; i < alignment.length; i++) {
			trgtSentence.get(i).setRelatedWord(srcSentence.get(alignment[i]));
		}
	}
	
	/**
	 * This inner type is a helper class for iterating through an alignment.
	 */
	public class AlignmentPair {
		
		private Word srcWord, trgtWord;
		private int pos;
		
		/**
		 * Sole constructor. Initializes the pair with source and target word.
		 * 
		 * @param srcWord source word
		 * @param trgtWord target word
		 * @param pos position of the target word
		 */
		public AlignmentPair(Word srcWord, Word trgtWord, int pos) {
			this.srcWord = srcWord;
			this.trgtWord = trgtWord;
			this.pos = pos;
		}

		/**
		 * Get the source word of this alignment pair.
		 * 
		 * @return the source word
		 */
		public Word getSourceWord() {
			return srcWord;
		}

		/**
		 * Get the target word of this alignment pair.
		 * 
		 * @return the target word
		 */
		public Word getTargetWord() {
			return trgtWord;
		}
		
		/**
		 * Get a candidate instance from this alignment pair. This instance 
		 * contains the target word, its position in the target sentence and
		 * a reference to the sentence pair itself
		 * 
		 * @return a candidate instance from this alignment pair
		 */
		public Candidate getCandidate() {
			return new Candidate(trgtWord, pos, SentencePair.this);
		}
	}
	
	/**
	 * Get the source sentence.
	 * 
	 * @return the source sentence
	 */
	public Sentence getSourceSentence() {
		return srcSentence;
	}

	/**
	 * Get the target sentence.
	 * 
	 * @return the target sentence
	 */
	public Sentence getTargetSentence() {
		return trgtSentence;
	}
	
	/**
	 * Get the alignment in integer vector representation.
	 * 
	 * @return the alignment
	 */
	public short[] getAlignment() {
		return alignment;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		// TODO: Output the alignment as well
		return srcSentence.toString() + "\n" + trgtSentence.toString();
	}

	@Override
	public Iterator<AlignmentPair> iterator() {
        Iterator<AlignmentPair> it = new Iterator<AlignmentPair>() {
        	private int pos = 0;
            @Override
            public boolean hasNext() {
            	return pos < alignment.length;
            }
            @Override
            public AlignmentPair next() {
            	AlignmentPair p = new AlignmentPair(
            			srcSentence.get(alignment[pos]),
            			trgtSentence.get(pos), pos);
            	pos++;
            	return p;
            }
            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
		return it;
	}
}
